package com.falcon.generator.vben.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.falcon.config.GeneratorConfig;
import com.falcon.generator.vben.domain.ftl.ApiEnum;
import com.falcon.generator.vben.domain.ftl.ApiFunction;
import com.falcon.generator.vben.domain.ftl.ApiType;
import com.falcon.generator.vben.domain.meta.ParamInfo;
import com.falcon.generator.vben.mapping.TsTypeMapping;
import com.falcon.parser.ControllerMetaParser;
import com.falcon.constants.FtlConstant;
import com.falcon.generator.vben.ITsTypeGenerator;
import com.falcon.domain.vo.ApiMethodMeta;
import com.falcon.domain.vo.ControllerMeta;
import com.falcon.utils.PathUtil;
import com.swan.core.utils.ClassUtil;
import com.swan.core.utils.NameUtil;
import com.swan.core.utils.TxtFileUtil;
import com.swan.freemarker.core.IFreemarkerTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class TsApiCodeGenerator extends AbsVbenGenerator {

    @Autowired
    private ControllerMetaParser controllerParser;

    @Autowired
    @Qualifier("freemarkerTemplateInside")
    protected IFreemarkerTemplate freemarkerTemplateInside;

    @Autowired
    private GeneratorConfig config;

    @Autowired
    private ITsTypeGenerator tsTypeGenerator;

    public void generateCode( Class clz) {
        // 解析 Controller 元数据信息
        ControllerMeta controllerMeta = controllerParser.parseController(clz, true, true, true);
        log.info("解析Controller");

        // 1.配置信息
        GeneratorConfig.VbenConfig vbenConfig = config.getVbenConfig();

        List<ApiFunction> apiFunctions = convertFunction(controllerMeta.getApiMethodMetas());

        List<ApiEnum> apiEnums = convertEnum(controllerMeta.getApiMethodMetas());

        // 生成接口所依赖的所有类型
        List<ApiType> apiTypes = convertType(controllerMeta.getApiMethodMetas());
        for (ApiType apiType : apiTypes) {
            Class apiTypeClass = ClassUtil.forName(apiType.getCanonicalName(), this.getClass().getClassLoader());
            this.tsTypeGenerator.generateCode(apiTypeClass);
        }

        boolean hasContentType = apiFunctions.stream().anyMatch(func -> "form".equals(func.getContentType()));
        boolean hasPagerResponse = apiFunctions.stream().anyMatch(func -> func.getResponse().startsWith("PagerResponse"));

        // 根据模板生成代码
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("controllerMeta", controllerMeta);

        // Api 枚举
        dataMap.put("apiEnums", apiEnums );
        dataMap.put("apiTypes", apiTypes);
        dataMap.put("apiFunctions", apiFunctions);

        dataMap.put("hasPagerResponse", hasPagerResponse);
        dataMap.put("hasContentType", hasContentType);

        // 生成代码
        String code = freemarkerTemplateInside.getContent(FtlConstant.FTL_VBEN_API, dataMap);

        // 文件路径
        String fileName = "/" + NameUtil.firstLowercase(StringUtils.substringBefore(controllerMeta.getSimpleName(), "Controller")) ;

        // 处理包结构
        String filePath = PathUtil.getPath(config.getProjectPath(), vbenConfig.getRelativeBasePath() + vbenConfig.getApiPath(), controllerMeta.getModuleName(), fileName, "Api.ts");

        // 写入文件
        TxtFileUtil.writeFile(filePath, true, code);
    }

    private List<ApiFunction> convertFunction(List<ApiMethodMeta> methods) {
        List<ApiFunction> functions = new ArrayList<>();

        for (ApiMethodMeta method : methods) {
            ApiFunction apiFunction = new ApiFunction();
            apiFunction.setName(method.getName());
            apiFunction.setDescription(method.getDescription());
            apiFunction.setHttpMethod(method.getHttpMethod().toLowerCase());
            apiFunction.setUriEnum(NameUtil.toHungaryName(method.getName()).toUpperCase());
            apiFunction.setContentType(method.isFormRequest() ? "form" : "json");

            if (CollectionUtil.isNotEmpty(method.getMethodParams())) {
                log.info("method:{}", method);
                ParamInfo paramInfo = method.getMethodParams().get(0);
                String simpleName = paramInfo.getClassType().getSimpleName();
                String tsType = Optional.ofNullable(TsTypeMapping.getTsType(simpleName)).orElse(simpleName);
                if (paramInfo.getType() == 3) {
                    switch (tsType){
                        case "String":
                            tsType = "string";
                            break;
                        case "Integer":
                        case "Long":
                        case "Double":
                        case "Short":
                            tsType = "number";
                            break;
                    }
                    tsType = "" + tsType + "[]";
                }
                apiFunction.setRequestParam(tsType);
            }
            if (CollectionUtil.isNotEmpty(method.getPathParams())) {
                String name  = method.getPathParams().get(0).getName();
                String type = TsTypeMapping.getTsType(method.getPathParams().get(0).getClassType().getSimpleName());
                apiFunction.setPathParam(new ApiFunction.PathParam(name, type));
            }
            if (Objects.nonNull(method.getReturnParam())) {
                String responseType = method.getReturnParam().getClassType().getSimpleName();

                responseType = Optional.ofNullable(TsTypeMapping.getTsType(responseType)).orElse(responseType);
                switch (method.getReturnParam().getType()) {
                    case 1:
                        responseType = "Array<" + responseType + ">";
                        break;
                    case 2:
                        responseType = "PagerResponse<" + responseType + ">";
                        break;
                }
                apiFunction.setResponse(responseType);
            }
            functions.add(apiFunction);
        }

        Collections.sort(functions, Comparator.comparing(ApiFunction::getName));

        return functions;
    }

    private List<ApiEnum> convertEnum(List<ApiMethodMeta> methods){
        List<ApiEnum> apiEnums = new ArrayList<>();

        for (ApiMethodMeta method : methods) {
            String enumName = NameUtil.toHungaryName(method.getName()).toUpperCase();

            String uri =  method.getRequestUri();
            uri = uri.replaceAll("\\{.*\\}", "");

            apiEnums.add(new ApiEnum(enumName, method.getDescription(), uri));
        }

        Collections.sort(apiEnums, Comparator.comparing(ApiEnum::getName));

        return apiEnums;
    }

    private List<ApiType> convertType(List<ApiMethodMeta> methods) {
        List<ApiType> apiTypes = new ArrayList<>();

        List<ParamInfo> params = methods.stream()
                .flatMap(methodMeta -> methodMeta.getMethodParams().stream())
                .filter(methodParam -> !TsTypeMapping.isBasicType(methodParam.getClassType().getSimpleName()))
                .collect(Collectors.toList());
        List<ParamInfo> result = methods.stream()
                .map(methodMeta -> methodMeta.getReturnParam())
                .filter(methodParam -> !TsTypeMapping.isBasicType(methodParam.getClassType().getSimpleName()))
                .filter(methodParam -> !methodParam.getClassType().getSimpleName().equals("void"))
                .collect(Collectors.toList());

        Set<ParamInfo> candidateParams = new HashSet<>();
        candidateParams.addAll(params);
        candidateParams.addAll(result);

        for (ParamInfo customType : candidateParams) {

            ApiType apiType = new ApiType();
            apiType.setName(customType.getClassType().getSimpleName());
            apiType.setCanonicalName(customType.getClassType().getCanonicalName());

            // 处理父类
            Class superclass = customType.getClassType().getSuperclass();
            if (!superclass.getSimpleName().equals("Object")) {
                apiType.setParentName(superclass.getSimpleName());
            }

            Field[] declaredFields = customType.getClassType().getDeclaredFields();

            for (Field declaredField : declaredFields) {
                ApiType.ApiField apiField = new ApiType.ApiField();
                apiField.setName(declaredField.getName());
                // 普通类型
                apiField.setType(TsTypeMapping.getTsType(declaredField.getType().getSimpleName()));
                apiField.setTypeClass(declaredField.getType());
                // list 类型或者 Set 类型
                if (declaredField.getType().isAssignableFrom(Set.class) || declaredField.getType().isAssignableFrom(List.class)) {
                    ParameterizedType paramType = (ParameterizedType) declaredField.getGenericType();
                    Type[] actualTypeArguments = paramType.getActualTypeArguments();
                    if (actualTypeArguments.length > 0) {
                        apiField.setTypeClass((Class) actualTypeArguments[0]);
                        apiField.setType(StringUtils.substringAfterLast(actualTypeArguments[0].getTypeName(), ".") + "[]");
                    }
                }
                // 无法正确推断时，设置为 any 类型兜底
                if (StringUtils.isEmpty(apiField.getType())) {
                    apiField.setType("any");
                }
                apiType.getFields().add(apiField);
            }
            apiTypes.add(apiType);
        }
        Collections.sort(apiTypes, Comparator.comparing(ApiType::getName));

        return apiTypes;
    }






}

